package com.sinosoft.brokerchannel.base.utile;


import com.alibaba.dubbo.common.utils.IOUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.sinosoft.brokerchannel.base.bins.BasChannelSet;
import com.sinosoft.brokerchannel.base.common.ChannelConstants;
import com.sinosoft.brokerchannel.base.dao.BasChannelSetDao;
import com.sinosoft.brokerchannel.base.dao.impl.BasChannelSetDaoImp;
import org.jdom2.Element;
import org.jdom2.output.XMLOutputter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.io.BufferedReader;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 需求名称：[非车理赔渠道平台]<br/>
 * 功能： 常用工具类<br/>
 * @author  shilk
 * @date 2017-11-17 15:08:52
 */
public class ToolUtils {
	private final static Logger logger = LoggerFactory.getLogger(ToolUtils.class);

	/**
	 * 对象校验
	 */
	public static Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

	/**
	 * 时间格式
	 */
	private final static String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";

	/**
	 * XML压缩
	 */
	private static Pattern p = Pattern.compile("[\\s&&[^\r\n]]*(?:[\r\n][\\s&&[^\r\n]]*)+");


	/**
	 * 私有构造器,防止静态方法不要new
	 */
	private ToolUtils() throws BusinessException {
		throw new BusinessException(ChannelConstants.ExceptionResult.NEWTOOLUTILS.getCode(),
				ChannelConstants.ExceptionResult.NEWTOOLUTILS.getMessage(),
				null);
	}


	/**
	 * 判断对象或对象数组中每一个对象是否为空: 对象为null，字符序列长度为0，集合类、Map为empty
	 *
	 * @param obj - 校验对象
	 * @return 校验结果
	 */
	public static boolean isNullOrEmpty(Object obj) {
		if (obj == null) {
			return true;
		}
		if (obj instanceof CharSequence) {
			return ((CharSequence) obj).length() == 0;
		}
		if (obj instanceof Collection) {
			return ((Collection<?>) obj).isEmpty();
		}
		if (obj instanceof Map) {
			return ((Map<?, ?>) obj).isEmpty();
		}
		if (obj instanceof Object[]) {
			Object[] object = (Object[]) obj;
			if (object.length == 0) {
				return true;
			}
			boolean empty = true;
			for(Object o : object){
				if (!isNullOrEmpty(o)) {
					empty = false;
					break;
				}
			}
			return empty;
		}
		return false;
	}



	/**
	 * 生成UUID<br/>
	 * 说明：<br/>
	 *     报文使用时，由请求方请求时生成，一直到整个业务的结束，使用同一个UUID<br/>
	 */
	public static String getUUID() {
		String uuid = UUID.randomUUID().toString();
		//去掉“-”符号
		return uuid.replaceAll("-", "");
	}

	/**
	 *
	 *  需求名称：[非车理赔渠道平台]<br/>
	 *  功能:[根据类.class 获取类名]<br/>
	 *  @author [@user] wem
	 *  @date 2017年9月20日 下午3:35:21
	 *  @param clazz 类Class
	 *  @return 类名称
	 */
	static String getClassName(Class<?> clazz) {
		return  new Object() {
			String getClassName() {
				String clazzName = clazz.getName();
				return clazzName.substring(clazzName.lastIndexOf('.')+1,clazzName.length());
			}
		}.getClassName();
	}

	/**
	 *
	 *  需求名称：[非车理赔渠道平台]<br/>
	 *  功能:[格式化日期]<br/>
	 *  @author [@wem]
	 *  @date 2017年9月20日 下午5:28:34
	 *
	 *  @param d  时间
	 *  @param format  格式
	 *  @return 格式化后的时间
	 */
	public static String formatDate(Date d, String format) {
		if (d == null) {
			return "";
		} else {
			return new SimpleDateFormat(format).format(d);
		}
	}

	/**
	 * 功能：获取当前时间（字符串，到时分秒）
	 * @author 石李鲲
	 * @date 2017-11-6 11:24:22
	 * @return 当前时间 格式yyyy-MM-dd HH:mm:ss
	 */
	public static String getSysDataTime() {
		return formatDate(new Date(), DATE_TIME_FORMAT);
	}

	/**
	 *
	 *  需求名称：[非车理赔渠道平台]<br/>
	 *  功能:[校验对象]<br/>
	 *  @author [shilk]
	 *  @date 2017年9月29日 下午8:30:19
	 *  功能说明：<br/>
	 *	   1.入参 - 被校验的对象；<br/>
	 *	   2.返参 - 校验结果。<br/>
	 */
	public static Map<String, Object> validateModel(Object obj) {
		//默认校验通过
		boolean flag = true;
		//用于存储验证后的错误信息
		StringBuilder buffer = new StringBuilder(64);
		//验证某个对象,，其实也可以只验证其中的某一个属性的
		Set<ConstraintViolation<Object>> constraintViolations = validator.validate(obj);
		Iterator<ConstraintViolation<Object>> iter = constraintViolations.iterator();
		while (iter.hasNext()) {
			String message = iter.next().getMessage();
			if(!flag) {
				buffer.append("\n");
			}
			flag = false;
			buffer.append(message);
		}

		Map<String,Object> map = new HashMap<>(16);
		//是否校验通过true 通过  false不通过
		map.put("flag", flag);
		//提示内容
		map.put("mess", buffer.toString());
		return map;
	}

	/**
	 *
	 *  需求名称：[非车理赔渠道平台]<br/>
	 *  功能:[获取异常日志信息]<br/>
	 *  @author [shilk]
	 *  @date 2017年10月17日 下午3:21:19
	 *
	 *  @param  e - 异常
	 *  @return 错误日志
	 */
	public static String getExceptionDetail(Exception e) {
		if(ToolUtils.isNullOrEmpty(e)){
			return null;
		}
		StringBuilder stringBuffer = new StringBuilder(e.toString());
		stringBuffer.append("\n");
		StackTraceElement[] messages = e.getStackTrace();
		for (StackTraceElement message : messages) {
			stringBuffer.append("\t").append(message.toString()).append("\n");
		}
		return stringBuffer.toString();
	}

	/**
	 * 功能:JSON字符串转换为XML<br/>
	 * @author 石李鲲
	 * @date 2017年11月6日09:59:47
	 * @param jsonStr-JSON字符串 ，encode-编码格式
	 * @return XML字符串
	 */
	static String json2Xml(String jsonStr, String encode){
		String rootNode = jsonStr.substring(jsonStr.indexOf("{\"") + 2, jsonStr.indexOf("\":"));
		JSONObject json = JSON.parseObject(jsonStr);
		json = (JSONObject) json.get(rootNode);
		if(json != null){
			Element elements=new Element(rootNode);
			getXMLFromObject(json,"",elements);
			XMLOutputter xmlOut = new XMLOutputter();
			String res=xmlOut.outputString(elements);
			return "<?xml version=\"1.0\" encoding=\""+encode+"\"?>" + res;
		}else{
			return null;
		}
	}

	/**
	 * 功能：循环读取JSON转换为XML元素<br/>
	 * @author 石李鲲
	 * @date 2017-11-6 10:03:17
	 * @param obj JSON节点
	 * @param tag 节点名称
	 * @param parent 当前解析节点
	 */
	private static void getXMLFromObject(Object obj,String tag,Element parent){
		if(obj==null) {
			return;
		}

		Element child;
		String eleStr;
		Object childValue;
		if(obj instanceof JSONObject)
		{
			JSONObject jsonObject=(JSONObject)obj;
			for(Object temp : jsonObject.keySet()){
				eleStr = temp.toString();
				childValue=jsonObject.get(eleStr);
				child=new Element(eleStr);
				if(childValue instanceof JSONArray) {
					getXMLFromObject(childValue,eleStr,parent);
				} else {
					parent.addContent(child);
					getXMLFromObject(childValue,eleStr,child);
				}
			}
		}else if(obj instanceof JSONArray){
			JSONArray jsonArray=(JSONArray)obj;
			for(Object childArray : jsonArray){
				childValue = childArray;
				child=new Element(tag);
				parent.addContent(child);
				getXMLFromObject(childValue,tag,child);
			}
		}else if(obj instanceof Date){
			SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			parent.setText(sf.format((Date)obj));
		}else{
			parent.setText(obj.toString());
		}
	}

	/**
	 * 需求名称: [大童保险理赔渠道平台]<br/>
	 * 功能: [根据渠道编码获取渠道信息]<br/>
	 * @author shilk
	 * @date 2017-12-13
	 * @param  channelNo-渠道编码
	 * @param  interfaceCode - 接口代码
	 * @param basChannelSetDaoImp - 渠道查询数据源
	 * @return {@link BasChannelSet}
	 */
	public static BasChannelSet getBasChannelInterface(String channelNo, String interfaceCode, BasChannelSetDaoImp basChannelSetDaoImp, BasChannelSetDao basChannelSetDao){
		Map<String, Object> params = new HashMap<>(16);
		params.put("channelCode", channelNo);
		params.put("interfaceCode", interfaceCode);
		String methodName = "selectChannelInterface";
		return basChannelSetDaoImp.getDateToMethod(params, methodName, BasChannelSetDaoImp.class, basChannelSetDao);
	}

	/**
	 * 需求名称: [大童保险理赔渠道平台]<br/>
	 * 功能: [获取接口的URL]<br/>
	 * @author shilk
	 * @date  2017-12-14
	 * @param basChannelSet-渠道信息
	 * @return [接口URL地址]
	 */
	public static String getChannelInterfaceURL(BasChannelSet basChannelSet){
		//容错判断
		if(ToolUtils.isNullOrEmpty(basChannelSet) || ToolUtils.isNullOrEmpty(basChannelSet.getBasInterfaceSet())){
			logger.error("未获取到渠道接口信息 getChannelInterfaceURL(BasChannelSet basChannelSet)");
			return null;
		}

		if(ToolUtils.isNullOrEmpty(basChannelSet.getSysUrl()) || ToolUtils.isNullOrEmpty(basChannelSet.getBasInterfaceSet().getReqUrl())){
			logger.error("未配置渠道URL或者接口地址，请检查数据库配置！");
			return null;
		}

		StringBuilder url = new StringBuilder();
		url.append(basChannelSet.getSysUrl());
		url.append(basChannelSet.getBasInterfaceSet().getReqUrl());

		return url.toString();
	}

	/**
	 * 需求名称: [大童保险理赔渠道平台]<br/>
	 * 功能: [获取当前时间戳]<br/>
	 * @author shilk
	 * @date  2017-12-14
	 * @return 时间戳
	 */
	public static Long getTimestamp(){
		return System.currentTimeMillis();
	}

	/**
	 * 需求名称: [大童保险理赔渠道平台]<br/>
	 * 功能: [对象复制]<br/>
	 * @author shilk
	 * @date
	 * @param target-复制目标对象
	 * @param source-复制数据源对象
	 */
	public static void copyProperties(Object target, Object source){
		BeanCopierUtils.copyProperties(target, source);
	}

	/**
	 * 需求名称: [大童保险理赔渠道平台]<br/>
	 * 功能: [获取IP地址]<br/>
	 * 创建时间: [2017-12-8 11:21]<br/>
	 * @author [shilk]
	 * @return [IP地址]
	 * 说明 [若无法获取，则返回未知]
	 */
	public static String getIpAddress(){
		return WebToolUtils.getLocalIP();
	}

	/**
	 * 需求名称: [大童保险理赔渠道平台]<br/>
	 * 功能: [将时间戳转换为时间字符串]<br/>
	 * @author shilk
	 * @date  2017-12-22 17:16:16
	 * @param timestamp 时间戳
	 * @return [时间字符串]
	 */
	public static String timestamp2Time(long timestamp){
		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		return format.format(timestamp);
	}


	/**
	 *  需求名称：[非车理赔渠道平台]<br/>
	 *  功能:[时间字符串转换为Date]<br/>
	 *  @author [fxg]
	 *  @date 2017年9月25日 下午2:14:14
	 */
	public static Date string2Date(String dataStr, String format) throws BusinessException   {
		if(StringUtils.isEmpty(dataStr)) {
			return null;
		} else {
			try {
				return new SimpleDateFormat(format).parse(dataStr);
			} catch (ParseException e) {
				logger.error("String2Date exception",e);
				throw new BusinessException(ChannelConstants.ExceptionResult.DATEFORMATERROR.getCode(),
						ChannelConstants.ExceptionResult.DATEFORMATERROR.getMessage(),
						e);
			}
		}
	}

	/**
	 *
	 *  需求名称：[非车理赔渠道平台]
	 *  功能:[将字符串转化为时间格式 含时分秒]
	 *  @author [shilk]
	 *  @date 2017年9月22日 上午11:05:04
	 *
	 */
	public static Date str2Datetime(String dateString) throws BusinessException {
		try {
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			return sdf.parse(dateString);
		} catch ( ParseException e) {
			logger.error("strToDatetime exception",e);
			throw new BusinessException(ChannelConstants.ExceptionResult.DATEFORMATERROR.getCode(),
					ChannelConstants.ExceptionResult.DATEFORMATERROR.getMessage()+e.getMessage(),
					 e);
		}
	}

	/**
	 *
	 *  需求名称：[非车理赔渠道平台]<br/>
	 *  功能:[实体类转json字符串   configure = true]<br/>
	 *  @author [shilk]
	 *  @date 2017年9月25日 下午1:50:42
	 *  功能说明：<br/>
	 *	   1.在需要加别名的字段上加@JsonProperty("xxx")注解，xxx为该字段别名。<br/>
	 */
	public static String obj2json(Object obj) throws BusinessException  {
		try {
			ObjectMapper mapper = new ObjectMapper();
			mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
			mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
			return mapper.writeValueAsString(obj);
		} catch (JsonProcessingException e) {
			logger.error("obj2json exception",e);
			throw new BusinessException(ChannelConstants.ExceptionResult.BEANTOJSONERROR.getCode(),
					ChannelConstants.ExceptionResult.BEANTOJSONERROR.getMessage(),
					e);
		}
	}


	/**
	 *
	 *  需求名称：[非车理赔渠道平台]<br/>
	 *  功能:[实体类转json字符串 configure = false]<br/>
	 *  @author [shilk]
	 *  @date 2017年10月12日 下午1:50:42
	 *  功能说明：<br/>
	 *	   1.在需要加别名的字段上加@JsonProperty("xxx")注解，xxx为该字段别名。<br/>
	 *
	 */
	public static String obj2jsonFalse(Object obj) throws BusinessException  {
		try {
			ObjectMapper mapper = new ObjectMapper();
			return mapper.writeValueAsString(obj);
		} catch (JsonProcessingException e) {
			logger.error("objectToJsonFalse exception",e);
			throw new BusinessException(ChannelConstants.ExceptionResult.BEANTOJSONERROR.getCode(),
					ChannelConstants.ExceptionResult.BEANTOJSONERROR.getMessage(),
					e);
		}
	}



	/**
	 *  需求名称：[非车理赔渠道平台]<br/>
	 *  功能:[将对象转换为XML]<br/>
	 *  @author [shilk]
	 *  @date 2017年10月27日 下午6:41:55
	 *  说明：若以格式化的方式输出XML，返回时修改代码 - return xstream.toXML(obj);
	 */
	public static String obj2xml(Object obj) throws BusinessException{
		try {
			String json = ToolUtils.obj2json(obj);
			return ToolUtils.json2Xml(json,"UTF-8");
		} catch (BusinessException e) {
			logger.error("convertToXml Exception",e);
			throw new BusinessException(ChannelConstants.ExceptionResult.JSON2XMLERROE.getCode(),
					ChannelConstants.ExceptionResult.JSON2XMLERROE.getMessage(),
					e);
		}
	}

	/**
	 * 功能：转换一个xml格式的字符串到json格式 <br/>
	 * @author shilk
	 * @date 2017-11-6 11:15:58
	 * @param xmlStr-xml格式的字符串
	 * @return 成功返回json 格式的字符串;失败反回null
	 */
	private static String xml2JSON(String xmlStr) throws BusinessException {
		if(ToolUtils.isNullOrEmpty(xmlStr)) {
			logger.info("xml2JSON(String xmlStr)入参："+xmlStr);
			return null;
		}
		try {
			//获取第一个节点名称
			Matcher m = p.matcher(xmlStr);
			String xml = m.replaceAll("");
			logger.info("压缩报文："+xml);
			//将XML转换为JSON字符串
			return XmlUtils.readXML(xml);
		} catch (BusinessException e) {
			logger.error("xml2JSON Exception",e);
			throw new BusinessException(ChannelConstants.ExceptionResult.XML2JSONERROR.getCode(),
					ChannelConstants.ExceptionResult.XML2JSONERROR.getMessage(),
					e);
		}
	}

	/**
	 * 需求名称：[非车理赔渠道平台]<br/>
	 * 功能:[将XML字符串转换为对象]<br/>
	 * @author [shilk]
	 * @date 2017年10月27日 下午6:37:11
	 */
	public static  <T> T xml2obj(Class<?> clazz, String xmlStr) throws BusinessException {
		String json;
		try {
			json = xml2JSON(xmlStr);
			@SuppressWarnings("unchecked")
			T obj = (T) json2objWithRoot(json, clazz);
			return obj;
		} catch (Exception e) {
			logger.error("convertXmlStrToObject Exception",e);
			throw new BusinessException(ChannelConstants.ExceptionResult.BEANTOXMLERROR.getCode(),
					ChannelConstants.ExceptionResult.BEANTOXMLERROR.getMessage(),
					e);
		}
	}

	/**
	 * 功能：将对象直接转换成String类型的 XML输出 （设置编码）
	 * @author shilk
	 * @date 2017年11月6日 11:05:49
	 * 说明：对象→JSON字符串→XML字符串
	 * @param obj 转换对象
	 * @return XML字符串
	 */
	public  String xml2obj(Object obj, String codeWay) throws BusinessException {
		String json = obj2json(obj);
		return ToolUtils.json2Xml(json,codeWay);
	}

	/**
	 * 功能：根据URL获取图片流数组<br/>
	 * 创建人：石李鲲<br/>
	 * 创建时间：2017-11-17 15:13:17<br/>
	 * @param imageURL 图片地址
	 * @return 文件流byte数组
	 */
	public static byte[] downloadImage(String imageURL) throws BusinessException {
		ImageUtils imageUtils = new ImageUtils();
		return imageUtils.downLoadFromUrl(imageURL);
	}


	/**
	 * 功能：从request中获取请求报文<br/>
	 * 创建人：shilk<br/>
	 * 创建时间：2017-11-22 15:46:54<br/>
	 * @param request-请求
	 */
	public static String getReqData(HttpServletRequest request) throws BusinessException {
		try {
			BufferedReader reader = request.getReader();
			String body = IOUtils.read(reader);
			reader.close();
			return body;
		} catch (IOException e) {
			logger.error("getReqData Exception",e);
			throw new BusinessException(ChannelConstants.ExceptionResult.LOADREQDATA.getCode(),
					ChannelConstants.ExceptionResult.LOADREQDATA.getMessage(), e);
		}
	}

	/**
	 *  需求名称：[非车理赔渠道平台]<br/>
	 *  功能:[json转实体类，含总节点名称]<br/>
	 *  @author [shilk]
	 *  @date 2017年9月25日 下午1:54:53
	 *  功能说明：<br/>
	 *	   1.在需要加别名的字段上加@JsonProperty("xxx")注解，xxx为该字段别名。<br/>
	 *
	 */
	public static <T> Object json2objWithRoot(String jsonStr,Class<T> clazz) throws BusinessException {
		try {
			ObjectMapper jsonToObjectMapper = new ObjectMapper();
			jsonToObjectMapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
			jsonToObjectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
			jsonToObjectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
			return jsonToObjectMapper.readValue(jsonStr, clazz);
		} catch ( IOException e) {
			logger.error("JSONToObj exception",e);
			throw new BusinessException(ChannelConstants.ExceptionResult.JSONTOBEANERROR.getCode(),
					ChannelConstants.ExceptionResult.JSONTOBEANERROR.getMessage(),
					e);
		}
	}

	/**
	 *  需求名称：[非车理赔渠道平台]<br/>
	 *  功能:[json转实体类]<br/>
	 *  @author [shilk]
	 *  @date 2017年9月25日 下午1:54:53
	 *  功能说明：<br/>
	 *	   1.在需要加别名的字段上加@JsonProperty("xxx")注解，xxx为该字段别名。<br/>
	 *
	 */
	public static <T> Object json2Obj(String jsonStr,Class<T> clazz) throws BusinessException {
		try {
			ObjectMapper jsonToObjectMapper = new ObjectMapper();
			jsonToObjectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
			jsonToObjectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
			return jsonToObjectMapper.readValue(jsonStr, clazz);
		} catch (IOException e) {
			logger.error("json2Obj exception",e);
			throw new BusinessException(ChannelConstants.ExceptionResult.JSONTOBEANERROR.getCode(),
					ChannelConstants.ExceptionResult.JSONTOBEANERROR.getMessage(),
					e);
		}
	}
}
